home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / LispExample / LispText.m < prev    next >
Text File  |  1995-06-12  |  12KB  |  485 lines

  1. /*
  2. **    LispText.m
  3. **    A Text object to drive the Lisp object.
  4. **    Lee Boynton, NeXT, Inc., 1989
  5. */
  6.  
  7.  
  8. #import <stdlib.h>
  9. #import <string.h>
  10. #import <sys/time.h>
  11. #import <appkit/Window.h>
  12. #import <appkit/publicWraps.h>
  13. #import "Lisp.h"
  14. #import "LispText.h"
  15.  
  16. @implementation LispText
  17.  
  18. #define DEFAULT_HISTORY 64
  19.  
  20. #define CLOSE_PAREN (')')
  21. #define OPEN_PAREN ('(')
  22. #define NEW_LINE ('\n')
  23. #define CTRL_A (1)
  24. #define CTRL_B (2)
  25. #define CTRL_C (3)
  26. #define CTRL_D (4)
  27. #define CTRL_E (5)
  28. #define CTRL_F (6)
  29. #define CTRL_K (11)
  30. #define CTRL_N (14)
  31. #define CTRL_P (16)
  32.  
  33.  
  34. unsigned short lispFilter(unsigned short theChar, int flags,
  35.                      unsigned short charSet)
  36. {
  37.     if (flags & NX_COMMANDMASK)
  38.     theChar = 0;
  39.     else if (theChar == NX_DELETE)
  40.     theChar = NX_BACKSPACE;
  41.     else if (theChar == NX_CR || theChar == NEW_LINE)
  42.     theChar = 0;
  43.     else if (theChar == '\t')
  44.     theChar = '\t';
  45.     else if (theChar < ' ')
  46.     theChar = 0;
  47.     return theChar;
  48. }
  49.  
  50. static void initLispCategoryTable(id self)
  51. {
  52.     static unsigned char lispTable[256];
  53.     unsigned char punctuationCategory, normalCategory;
  54.     unsigned char nullCategory, whiteSpaceCategory;
  55.     int i;
  56.  
  57.     bcopy([self charCategoryTable],lispTable,sizeof(lispTable));
  58.     nullCategory = lispTable[0];
  59.     punctuationCategory = lispTable[';'];
  60.     normalCategory = lispTable['a'];
  61.     whiteSpaceCategory = lispTable[' '];
  62.     lispTable['!'] = normalCategory;
  63.     lispTable['&'] = normalCategory;
  64.     lispTable['('] = punctuationCategory;
  65.     lispTable[')'] = punctuationCategory;
  66.     lispTable['*'] = normalCategory;
  67.     lispTable['+'] = normalCategory;
  68.     lispTable['-'] = normalCategory;
  69.     lispTable['.'] = normalCategory;
  70.     lispTable['/'] = normalCategory;
  71.     lispTable[':'] = normalCategory;
  72.     lispTable['<'] = normalCategory;
  73.     lispTable['='] = normalCategory;
  74.     lispTable['>'] = normalCategory;
  75.     lispTable['?'] = normalCategory;
  76.     lispTable['@'] = normalCategory;
  77.     lispTable['['] = punctuationCategory;
  78.     lispTable['\\'] = normalCategory;
  79.     lispTable[']'] = punctuationCategory;
  80.     lispTable['{'] = punctuationCategory;
  81.     lispTable['|'] = normalCategory;
  82.     lispTable['}'] = punctuationCategory;
  83.     for(i=128;i<256;i++) lispTable[i] = nullCategory;
  84.     [self setCharCategoryTable:lispTable];
  85. }
  86.  
  87. + newFrame:(NXRect *)frameRect text:(char *)theText alignment:(int)align
  88. {
  89.     int i;
  90.     self = [super newFrame:frameRect text:theText alignment:align];
  91.     [self setCharFilter:lispFilter];
  92.     [self setOpaque:YES];
  93.     [self setOverstrikeDiacriticals:NO];
  94.     initLispCategoryTable(self);
  95.     inputPosition = 0;
  96.     maxHistory = DEFAULT_HISTORY;
  97.     history = (char **)malloc(maxHistory*sizeof(char*));
  98.     for (i=0; i<maxHistory; i++) history[i] = NULL;
  99.     historyHead = 0;
  100.     connectedToLisp=NO;
  101.     theLisp = [Lisp new];
  102.     [theLisp setDelegate:self];
  103.     return self;
  104. }
  105.  
  106. - free
  107. {
  108.     [theLisp free];
  109.     return [super free];
  110. }
  111.  
  112. - lispOutput:(const char *)theString
  113. {
  114.     int offset;
  115.     NXSelPt selStart, selEnd;
  116.     if (!theString) return self;
  117.     offset = strlen(theString);
  118.     if (!connectedToLisp) {
  119.     int i = [self textLength];
  120.     [self setSel:0 :i];
  121.     [self replaceSel:theString];
  122.     inputPosition = offset;
  123.     [window makeFirstResponder:self];
  124.     connectedToLisp=YES;
  125.     } else {
  126.     [self getSel:&selStart :&selEnd];
  127.     [self setSel:inputPosition :inputPosition];
  128.     [self replaceSel:theString];
  129.     inputPosition += offset;
  130.     [self setSel:selStart.cp+offset :selEnd.cp+offset];
  131.     }
  132.     [self scrollSelToVisible];
  133.     return self;
  134. }
  135.  
  136. /*
  137. ** NXBGetc - a text stream macro to get the previous character.
  138. */
  139.  
  140. typedef struct {
  141.     id text;
  142.     NXTextBlock *block;
  143. } textInfo;
  144.  
  145. static char getPrevious(NXStream *s)
  146. {
  147.     textInfo *info = (textInfo *) s->info;
  148.  
  149.     NXTextBlock *block = info->block->prior;
  150.     if (!block) {
  151.     return EOF;
  152.     }
  153.     s->buf_base = block->text;
  154.     s->buf_ptr = s->buf_base + block->chars;
  155.     s->offset -= block->chars;
  156.     info->block = block;
  157.     return *(--s->buf_ptr);
  158. }
  159.  
  160. #define NXBGetc(s) \
  161.     (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
  162.     *(--((s)->buf_ptr)) )
  163.  
  164.  
  165. int findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos)
  166. {
  167.     int count = 1;
  168.     char ch;
  169.     NXStream *s = [text stream];
  170.     NXSeek(s, curPos, NX_FROMSTART);
  171.     while ((ch = NXBGetc(s)) != EOF) {
  172.     if (ch == thatChar && !(--count))
  173.         return NXTell(s);
  174.     else if (ch == thisChar)
  175.         count++;
  176.     }
  177.     return -1;
  178. }
  179.  
  180. int findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos)
  181. {
  182.     int count = 1;
  183.     char ch;
  184.     NXStream *s = [text stream];
  185.     NXSeek(s, curPos, NX_FROMSTART);
  186.     while ((ch = NXGetc(s)) != EOF) {
  187.     if (ch == thatChar && !(--count))
  188.         return NXTell(s);
  189.     else if (ch == thisChar)
  190.         count++;
  191.     }
  192.     return -1;
  193. }
  194.  
  195. void highlightChar(id text, int charPosition)
  196. {
  197.     NXSelPt selStart, selEnd;
  198.     [text getSel:&selStart :&selEnd];
  199.     [text setSel:charPosition :charPosition+1];
  200.     [[text window] flushWindow];
  201.     NXPing();
  202.     [[text window] disableFlushWindow];
  203.     [text setSel:selStart.cp :selEnd.cp];
  204.     [[text window] reenableFlushWindow];
  205. }
  206.  
  207. int searchBackwardsForChar(id text, char theChar, int curPos)
  208. {
  209.     char ch;
  210.     NXStream *s = [text stream];
  211.     NXSeek(s, curPos, NX_FROMSTART);
  212.     
  213.     while ((ch = NXBGetc(s)) != EOF) {
  214.     if (ch == theChar)
  215.         return NXTell(s);
  216.     }
  217.     return -1;
  218. }
  219.  
  220. void insertChar(id self, char c, int pos)
  221. {
  222.     char buf[2];
  223.     buf[0] = c;
  224.     buf[1] = 0;
  225.     [self setSel:pos :pos];
  226.     [self replaceSel:buf];
  227.     [self scrollSelToVisible];
  228. }
  229.  
  230. static void addToHistory(id self, char *buf)
  231. {
  232.     int i = (self->historyHead++) % self->maxHistory;
  233.     if (self->history[i])
  234.     free(self->history[i]);
  235.     self->history[i] = buf;
  236. }
  237.  
  238. static char *getHistory(id self, int index)
  239. {
  240.     int i = index % self->maxHistory;
  241.     return self->history[i];
  242. }
  243.  
  244. - keyDown:(NXEvent *)theEvent
  245. {
  246.     static int history_index=0;
  247.     int i, lastPos = [self textLength];
  248.     unsigned short val;
  249.     NXSelPt selStart, selEnd;
  250.     if (!connectedToLisp) {
  251.     NXBeep();
  252.     return self;
  253.     }
  254.     [self getSel:&selStart :&selEnd];
  255.     val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
  256.     switch (val) {
  257.     case CLOSE_PAREN:
  258.         if (selStart.cp < inputPosition)
  259.         [self setSel:lastPos :lastPos];
  260.         i = findMatchingOpenParen(self,CLOSE_PAREN,OPEN_PAREN,selEnd.cp);
  261.         [super keyDown:theEvent];
  262.         if (i >= 0)
  263.         highlightChar(self,i);
  264.         return self;
  265.     case NX_DELETE:
  266.     case NX_BACKSPACE:
  267.         if (selEnd.cp == selStart.cp) {
  268.         if (lastPos <= inputPosition || selStart.cp <= inputPosition) {
  269.             NXBeep();
  270.             return self;
  271.         }
  272.         } else {
  273.         if (selEnd.cp <= inputPosition ||
  274.                      selStart.cp <= inputPosition) {
  275.             NXBeep();
  276.             return self;
  277.         }
  278.         }
  279.         break;
  280.     case NX_CR:
  281.     case NEW_LINE:
  282.         if (selEnd.cp >= inputPosition) {
  283.         char *buf;
  284.         i = lastPos - inputPosition;
  285.         buf = (char *)malloc(i+2);
  286.         if (i)
  287.             [self getSubstring:buf start:inputPosition length:i];
  288.         buf[i] = NEW_LINE;
  289.         buf[i+1] = 0;
  290.         inputPosition = lastPos+1;
  291.         [theLisp inputLisp:buf];
  292.         if (i) {
  293.             buf[i] = 0;
  294.             addToHistory(self,buf);
  295.             history_index = historyHead;
  296.         }
  297.         [self setSel:lastPos :lastPos];
  298.         insertChar(self,NEW_LINE,lastPos);
  299.         return self;
  300.         } else {
  301.         int len = selEnd.cp-selStart.cp;
  302.         if (len) {
  303.             char *buf = (char *)malloc(len+1);
  304.             [self getSubstring:buf start:selStart.cp length:len];
  305.             buf[len] = 0;
  306.             [self setSel:lastPos :lastPos];
  307.             [self replaceSel:buf];
  308.             free(buf);
  309.             [self scrollSelToVisible];
  310.         } else
  311.             [self setSel:lastPos :lastPos];
  312.         return self;
  313.         }
  314.     case CTRL_C:
  315.         [self setSel:lastPos :lastPos];
  316.         [theLisp interruptLisp];
  317.         return self;
  318.         break;
  319.     case CTRL_A:
  320.         [self setSel:inputPosition :inputPosition];
  321.         return self;
  322.     case CTRL_E:
  323.         [self setSel:lastPos :lastPos];
  324.         return self;
  325.     case CTRL_B:
  326.         if (selStart.cp < inputPosition)
  327.         [self setSel:inputPosition :inputPosition];
  328.         else if (selStart.cp != selEnd.cp)
  329.         [self setSel:selStart.cp :selStart.cp];
  330.         else if (selStart.cp > inputPosition)
  331.         [self setSel:selStart.cp-1 :selStart.cp-1];
  332.         else
  333.         NXBeep();
  334.         return self;
  335.     case CTRL_F:
  336.         if (selEnd.cp < inputPosition)
  337.         [self setSel:inputPosition :inputPosition];
  338.         else if (selStart.cp != selEnd.cp)
  339.         [self setSel:selEnd.cp :selEnd.cp];
  340.         else if (selEnd.cp < lastPos)
  341.         [self setSel:selEnd.cp+1 :selEnd.cp+1];
  342.         else
  343.         NXBeep();
  344.         return self;
  345.     case CTRL_D:
  346.         if (selStart.cp >= inputPosition) {
  347.         if (selStart.cp == selEnd.cp)
  348.             if (selEnd.cp >= lastPos) {
  349.             NXBeep();
  350.             return;
  351.             } else
  352.             [self setSel:selEnd.cp :selEnd.cp+1];
  353.         [self replaceSel:""];
  354.         } else
  355.         NXBeep();
  356.         return self;
  357.     case CTRL_K:
  358.         if (selStart.cp >= inputPosition) {
  359.         if (inputPosition != lastPos)
  360.             [self setSel:selStart.cp :lastPos];
  361.         [self replaceSel:""];
  362.         } else
  363.         NXBeep();
  364.         return self;
  365.     case CTRL_P: {
  366.         char *s;
  367.         if (!historyHead || 
  368.             (history_index <= (historyHead-self->maxHistory)))
  369.         return self;
  370.         s = getHistory(self, history_index-1);
  371.         if (!s) return self;
  372.         history_index--;
  373.         [window disableFlushWindow];
  374.         [self setSel:inputPosition :lastPos];
  375.         [self replaceSel:s];
  376.         [self scrollSelToVisible];
  377.         [window reenableFlushWindow];
  378.         [window flushWindow];
  379.         return self; }
  380.     case CTRL_N:
  381.         {
  382.         char *s;
  383.         if (!historyHead || (history_index >= (historyHead)))
  384.         return self;
  385.         s = getHistory(self, history_index);
  386.         if (s)
  387.         s = getHistory(self, ++history_index);
  388.         [window disableFlushWindow];
  389.         [self setSel:inputPosition :lastPos];
  390.         [self replaceSel:s];
  391.         [self scrollSelToVisible];
  392.         [window reenableFlushWindow];
  393.         [window flushWindow];
  394.         return self; }
  395.     default:
  396.         if (selStart.cp < inputPosition)
  397.         [self setSel:lastPos :lastPos];
  398.         break;
  399.     }
  400.     return [super keyDown:theEvent];
  401. }
  402.  
  403. - clear:sender
  404. {
  405.     NXSelPt selStart, selEnd;
  406.     [self getSel:&selStart :&selEnd];
  407.     if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
  408.     return self;
  409.     else
  410.     return [super clear:sender];
  411. }
  412.  
  413. - cut:sender
  414. {
  415.     NXSelPt selStart, selEnd;
  416.     [self getSel:&selStart :&selEnd];
  417.     if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
  418.     return self;
  419.     else
  420.     return [super cut:sender];
  421. }
  422.  
  423. - paste:sender
  424. {
  425.     int size = [self textLength];
  426.     NXSelPt selStart, selEnd;
  427.     [self getSel:&selStart :&selEnd];
  428.     if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
  429.     [self setSel:size :size];
  430.     return [super paste:sender];
  431. }
  432.  
  433. - mouseDown: (NXEvent *) theEvent
  434. {
  435.     BOOL doubleClick;
  436.     doubleClick = (theEvent->data.mouse.click == 2);
  437.     [super mouseDown:theEvent];
  438.     if (theEvent->data.mouse.click == 2) {
  439.     char theChar;
  440.     int i;
  441.     NXSelPt selStart, selEnd;
  442.     [self getSel:&selStart :&selEnd];
  443.     [self getSubstring:&theChar start:selStart.cp length:1];
  444.     if (theChar == OPEN_PAREN) {
  445.         i = findMatchingCloseParen(self,theChar,CLOSE_PAREN,selEnd.cp+1);
  446.         if (i >= 0)
  447.         [self setSel:selStart.cp :i];
  448.     } else if (theChar == CLOSE_PAREN) {
  449.         i = findMatchingOpenParen(self,theChar,OPEN_PAREN,selEnd.cp-1);
  450.         if (i >= 0)
  451.         [self setSel:i :selEnd.cp];
  452.     }
  453.     }
  454.     return self;
  455. }
  456.  
  457. - clearAll:sender
  458. {
  459.     [self setSel:0 :[self textLength]];
  460.     [super clear:self];
  461.     inputPosition = 0;
  462.     return self;
  463. }
  464.  
  465. - inputLisp:(const char *)theString
  466. {
  467.     return [theLisp inputLisp:theString];
  468. }
  469.  
  470.  
  471. - interruptLisp
  472. {
  473.     return [theLisp interruptLisp];
  474. }
  475.  
  476.     
  477.  
  478. @end
  479.  
  480.  
  481.  
  482.  
  483.  
  484.  
  485.